﻿using System.Windows.Controls;
using System.Collections;
using DeepEarth.Map.Core;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Data;
using System.Collections.Specialized;
using System.Collections.ObjectModel;
using System;
using DeepEarth.Core.Persistence;
using System.Windows.Input;
using DeepEarth.Core.Controls;
using DeepEarth.Core.Data;
using DeepEarth.Core;
namespace DeepEarth.Toolkit.Controls
{
    public class LayerPanel : TreeView
    {
        public static readonly DependencyProperty TemplateSelectorProperty =
            DependencyProperty.Register("TemplateSelector", typeof(TemplateSelector), typeof(LayerPanel), null);

        public TemplateSelector TemplateSelector
        {
            get { return (TemplateSelector)GetValue(TemplateSelectorProperty); }
            set { SetValue(TemplateSelectorProperty, value); }
        }

        private static void GeoItemsChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
        {
            if (args.NewValue != null)
            {
                LayerPanel self = sender as LayerPanel;
                GeoItems geoItems = args.NewValue as GeoItems;

                self.ItemsSource = geoItems.ViewModels;
            }
        }

        public GeoItems GeoItems
        {
            get { return (GeoItems)GetValue(GeoItemsProperty); }
            set { SetValue(GeoItemsProperty, value); }
        }

        // Using a DependencyProperty as the backing store for GeoItems.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty GeoItemsProperty =
            DependencyProperty.Register("GeoItems", typeof(GeoItems), typeof(LayerPanel), new PropertyMetadata(GeoItemsChanged));

               
        public static readonly DependencyProperty LayerPanelItemSelectedCommandProperty =
            DependencyProperty.Register("LayerPanelItemSelectedCommand", typeof(ICommand), typeof(LayerPanel), null);

        public ICommand LayerPanelItemSelectedCommand
        {
            get { return (ICommand)GetValue(LayerPanelItemSelectedCommandProperty); }
            set { SetValue(LayerPanelItemSelectedCommandProperty, value); }
        }
        
        #region Persistence

        // A Bare bones view model for persistence
        // public for serialization
        public class PersistenceViewModel
        {
            public IComparable Id { get; set; } 
            public bool IsExpanded { get; set; }
            public bool IsVisible { get; set; }
        }

        private const string PersistencePropertyKey = "ViewModelStates";
        private string persistenceName;

        private bool HasPersistenceName
        {
            get
            {
                this.persistenceName = Persist.GetName(this);
                return !string.IsNullOrEmpty(persistenceName);
            }
        }

        private static readonly DependencyProperty ItemsSourceListenerProperty =
            DependencyProperty.Register("ItemsSourceListener", typeof(IEnumerable), typeof(LayerPanel), new PropertyMetadata(ItemsSourcePropertyChanged));


        private static void ItemsSourcePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
        {
            LayerPanel self = sender as LayerPanel;

            if (args.NewValue != null)
            {
                IEnumerable itemsEnumerable = args.NewValue as IEnumerable;

                INotifyCollectionChanged observableCollection = args.NewValue as INotifyCollectionChanged;
                observableCollection.CollectionChanged += self.ItemsSourceChanged;

                self.Restore();
            }
        }

        private void ItemsSourceChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            Restore();
        }

        private void Restore()
        {
            string serialized = Persist.Storage[persistenceName, PersistencePropertyKey];
            ObservableCollection<GeoItemViewModel> viewModels = ItemsSource as ObservableCollection<GeoItemViewModel>;

            if (!string.IsNullOrEmpty(serialized) && viewModels != null)
            {
                List<PersistenceViewModel> lightViewModels = JsonSerializer.Deserialize<List<PersistenceViewModel>>(serialized);

                Dictionary<IComparable, PersistenceViewModel> lookup = new Dictionary<IComparable, PersistenceViewModel>();

                foreach (PersistenceViewModel lite in lightViewModels)
                {
                    lookup.Add(lite.Id, lite);
                }

                SetLayerViewModelProperty(viewModels, lookup);
            }
        }

        private void SaveState()
        {
            if (HasPersistenceName)
            {
                string serialized = Serialize();
                Persist.Storage[persistenceName, PersistencePropertyKey] = serialized;
            }
        }


        private string Serialize()
        {
            ObservableCollection<GeoItemViewModel> viewModels = ItemsSource as ObservableCollection<GeoItemViewModel>;

            if (viewModels != null)
            {
                List<PersistenceViewModel> lightViewModels = new List<PersistenceViewModel>();

                GetLayerViewModels(viewModels, lightViewModels);

                return JsonSerializer.Serialize(lightViewModels);

            }

            return null;
        }

        private void GetLayerViewModels(ObservableCollection<GeoItemViewModel> source, List<PersistenceViewModel> acc)
        {
                foreach (GeoItemViewModel viewModel in source)
                {
                    if (viewModel.Data is IItemsContainer && viewModel.Data is ISearchable)
                    {
                        ISearchable searchable = viewModel.Data as ISearchable;

                        PersistenceViewModel lite = new PersistenceViewModel
                        {
                            Id = searchable.Id,
                            IsVisible = viewModel.IsActive,
                            IsExpanded = viewModel.IsExpanded
                        };
                        acc.Add(lite);

                        GetLayerViewModels(viewModel.Children, acc);
                    }
                }
        }

        private void SetLayerViewModelProperty(IEnumerable<GeoItemViewModel> source, Dictionary<IComparable, PersistenceViewModel> lookup)
        {
            foreach (GeoItemViewModel viewModel in source)
            {
                if (viewModel.Data != null && viewModel.Data is ILayerDefinition && viewModel.Data is ISearchable)
                {
                    ISearchable searchable = viewModel.Data as ISearchable;

                    if (lookup.ContainsKey(searchable.Id))
                    {
                        PersistenceViewModel lite = lookup[searchable.Id];

                        viewModel.IsExpanded = lite.IsExpanded;
                        viewModel.IsActive = lite.IsVisible;
                    }

                    SetLayerViewModelProperty(viewModel.Children, lookup);
                }
            }
        }

        #endregion


        public LayerPanel()
        {
            DefaultStyleKey = typeof(LayerPanel);

            Application.Current.Exit += (o, e) =>
                {
                    SaveState();
                };
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            if (this.HasPersistenceName)
            {
                Binding itemsSourceBinding = new Binding("ItemsSource") { Source = this, Mode = BindingMode.OneWay };
                this.SetBinding(ItemsSourceListenerProperty, itemsSourceBinding);
            }     
        }

        protected override bool IsItemItsOwnContainerOverride(object item)
        {
            return item is LayerPanelItem;
        }

        protected override DependencyObject GetContainerForItemOverride()
        {
            LayerPanelItem lpi = new LayerPanelItem();
            lpi.ItemTemplate = this.ItemTemplate;
            lpi.TemplateSelector = this.TemplateSelector;
            lpi.LayerPanelItemSelectedCommand = this.LayerPanelItemSelectedCommand;
            return lpi;
        }

        protected override void PrepareContainerForItemOverride(System.Windows.DependencyObject element, object item)
        {
            GeoItemViewModel model = item as GeoItemViewModel;

            if (this.TemplateSelector != null)
            {
                DataTemplate myTemplate = this.TemplateSelector.FindTemplate<DataTemplate>(model.Data);

                if (myTemplate == null)
                {
                    myTemplate = this.ItemTemplate;
                }

                TreeViewItem itemCon = element as TreeViewItem;
                if (itemCon != null)
                {
                    itemCon.HeaderTemplate = myTemplate;
                }
            }

            base.PrepareContainerForItemOverride(element, model.Data);

            LayerPanelItem.PrepareContainerForViewModel(element, model);

            //// TODO: Move persistence monitoring into PrepareContainerForViewModel

            //if (model.Data is ILayerDefinition && model.Data is ISearchable)
            //{
            //    model.PropertyChanged += (o, e) =>
            //        {                       
            //            if (!suspendSave && e.PropertyName == MapItemViewModel.ISVISIBLE || e.PropertyName == MapItemViewModel.SHOW_SHOWCHILDREN)
            //            {
            //                this.SaveState();
            //            };
            //        };
            //}
        }
    }
}
